package stmlib;
import java.util.*;

/** A Transactional Set.  Protected by a read-write lock. */
public class StmSet<E> extends Lockable implements Set<E> {
    final Set <E> set;
    public StmSet(Set<E> source, LockManager lm) {this.set = source; this.lm = lm;}
    public StmSet() { this.set = new HashSet<E>(); this.lm = new LockManager(); }
    public boolean add(final E e) {
        lm.writeLock();
        final boolean changed = set.add(e);
        if (changed) lm.logUndo(new Runnable(){
            public void run() { set.remove(e); }
        });
        return changed;
    }
    public boolean addAll(Collection<? extends E> c)  {
        lm.writeLock();
        boolean changed = false;
        for(E e: c) { if (set.add(e)) changed = true; }
        return changed;
    }
    public void clear() { 
        lm.writeLock();
        final Set<E> oldContents = new HashSet<E>();
        oldContents.addAll(set);
        set.clear();
        lm.logUndo(new Runnable(){ public void run() {
            set.addAll(oldContents);
        }});
    }

    public boolean contains(Object o) { lm.readLock(); return set.contains((E)o); }
    public boolean containsAll(Collection<?> c) {
        lm.readLock(); return set.containsAll(c);
    }
    public boolean isEmpty() { lm.readLock(); return set.isEmpty(); }

    /** Todo: iterator that allows .remove */
    public Iterator<E> iterator() { return new StmIterator<E>(set.iterator(), lm); }
    public boolean remove(final Object o) {
        lm.writeLock();
        final boolean changed = set.remove((E)o);
        if (changed) lm.logUndo(new Runnable() {
            public void run() { set.add((E)o); }
        });
        return changed;
    }
    public boolean removeAll(Collection<?> c) {
        boolean modified = false;
        for(Object o: c) { if(remove((E)o)) modified = true; } //delegate locking to remove();
        return modified;
    }

    public boolean retainAll(Collection<?> collection) {
        lm.writeLock();
        boolean modified = true;
        for(Object o: set) {
            if (collection.contains(o)) { set.remove((E)o); modified = true; }
        }
        return modified;
    }
    public int size() { lm.readLock(); return set.size(); }
    public Object[] toArray() { lm.readLock(); return set.toArray(); }
    public <T> T[] toArray(T[] contents) { lm.readLock(); return set.toArray(contents);}
    public String toString() { return toString(set); }
    public StmSet<E> lock() { lm.writeLock(); return this; }
}